package com.example.myproject.zebra.demo1;

import javax.print.*;
import javax.print.attribute.standard.PrinterName;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;

/**
 * https://blog.csdn.net/liruidong01/article/details/123249342
 * 高线挂牌机器人打印使用
 * 中文方式几种支持： 1 点阵支持，2 字体支持，3 把中文转成图片，然后在写图片。
 * 直接使用字体支持中文
 *
 *  * 打印中文，三种方式，
 *  * 1种ts24 点阵库, 无法更改字体样式，什么字体不支持，
 *  * 只能修改字体倍率
 *  * 第2 种是把中文做图片，
 *  * 3 字体库支持
 *
 * 2022.3.3
 * @author lrd
 *
 */
public class ZplPrinterRobot {
    private String printerURI = null;//打印机完整路径
    private PrintService printService = null;//打印机服务
    private byte[] dotFont;
    private String begin = "^XA";	//标签格式以^XA开始
    private String end = "^XZ";		//标签格式以^XZ结束
    private String content = "";

    /**
     * 中文字符、英文字符(包含数字)混合 转90度
     */
    public void setDpi() {
        //content += "^JMA";
        content +="^LH0,0";
    }

    //RealZDesignerZT411
    public static void main(String[] args) {
        //加载字体
//        File file = new File("D://ts24.lib");
//        if(file.exists()){
//            FileInputStream fis;
//            try {
//                fis = new FileInputStream(file);
//                byte[] dotFont = new byte[fis.available()];
//                fis.read(dotFont);
//                fis.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }else{
//            System.out.println("D://ts24.lib文件不存在");
//            throw new RRException("D://ts24.lib文件不存在");
//        }

        test();


    }

    private static void test(){
        //初始化打印机
        PrintService[] services = PrintServiceLookup.lookupPrintServices(null,null);
        System.out.println(Arrays.toString(services));
        String printName = "RealZDesignerZT411";
        ZplPrinterRobot p = new ZplPrinterRobot(printName);

        //p.setDpi();
        SysPlanBetEntity planBet = new SysPlanBetEntity();
        planBet.setTargetName("钢筋混凝土用热轧带肋钢筋");
        planBet.setMark("HRB400E");
        planBet.setNorms("Φ10");
        planBet.setMatNo("123456789012");
        planBet.setLicNo("XK05-001-00727");
        planBet.setExeSta("GB/T1499.2-2018");
        planBet.setTaskId("D1740001");
        planBet.setMatPraWei(new BigDecimal(12300));
        planBet.setClassCode("甲jia");

        String targetName = planBet.getTargetName();
        if(targetName.length() > 6){
            String targetName1 = targetName.substring(0,6);
            p.setTextR(targetName1, 120, 250, 35, 30);
            String targetName2=targetName.substring(6);
            p.setTextR(targetName2, 155, 250, 35, 30);
        }else{
            p.setTextR(planBet.getTargetName(), 130, 250, 40, 40);           //产品名称：钢筋混凝土用热轧带肋钢筋（系统带出）
        }
        p.setTextR("HRB400E",215, 250, 40, 40);                 //牌    号：HRB400
        p.setTextR(planBet.getNorms(), 300, 250, 40, 40);                // 规    格：Φ8
        p.setTextR("支数1211212122", 380, 250, 40, 40);                //支数/捆号：D174000101（注：对应系统里的材料号）870
//	SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd");
        p.setTextR("甲1211212122", 465, 250, 40, 40);                //  日期789  810
        p.setTextR(planBet.getLicNo(), 550, 250, 40, 40);                //  许可证号：XK05-001-00276（系统带出）--------630  600

        //条码    "^CWJ,E:ANMDS.TTF^FB600,1,0,L,0^LH0,0"+"^FO"+x+","+y+"^AJB,"+h+","+w+"^CI28^FD"+str+"^FS";
        String bar2 = "D221048003";//12位
        String bar2Paper = "^FO620,550^BY2,3.0,80^FB600,1,0,L,0^BCB,,Y,N,N^FD${data}^FS";//条码样式模板
        p.setBarcode(bar2,bar2Paper);

        p.setTextLT("GB1499.2-2008", 160, 340, 40, 40);               //执行标准：GB1499.2 （注：不带年份）
        p.setTextLT("D1740001", 240, 340, 40, 40);				  // 炉 批 号：D1740001（注：对应系统里的轧制批号）
        p.setTextLT("/", 330, 340, 40, 40);//炉号 30
        p.setTextLT("2334", 410, 340, 40, 40);        //重    量：实际重量 20

        //二维码
//        String bar3 ="产品名称:"+planBet.getTargetName()+" 重量:2333kg"+"规格:"+planBet.getNorms()+"牌号:"+planBet.getMark();//12位^CI26
//        String bar3Paper ="^FO520,70^BQ,2,3^CI26^FH^FDQA,${data}^FS"; //二维码样式模板
//        p.setBarcode(bar3,bar3Paper);


        String zpl = p.getZpl();
        System.out.println("zpl:"+zpl);
        boolean result = p.print(zpl);//打印
        System.out.println(result);
        if(!result){
            throw new RRException("第一次打印出错");
        }
        p.resetZpl();//注意要清除上次的打印信息
    }


    public ZplPrinterRobot(){

    }
    /**
     * 构造方法
     * @param printerURI 打印机路径
     */
    public ZplPrinterRobot(String printerURI){
        //加载字体
//        File file = new File("D://ts24.lib");
//        if(file.exists()){
//            FileInputStream fis;
//            try {
//                fis = new FileInputStream(file);
//                dotFont = new byte[fis.available()];
//                fis.read(dotFont);
//                fis.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }else{
//            System.out.println("D://ts24.lib文件不存在");
//            throw new RRException("D://ts24.lib文件不存在");
//        }

        //初始化打印机
        PrintService[] services = PrintServiceLookup.lookupPrintServices(null,null);
        if (services != null && services.length > 0) {
            for (PrintService service : services) {
                if (service.getName().contains(printerURI)) {
                    printService = service;
                    break;
                }
            }
        }
        if (printService == null) {
            System.out.println("没有找到打印机：["+printerURI+"]");
            //循环出所有的打印机
            if (services != null && services.length > 0) {
                System.out.println("可用的打印机列表：");
                for (PrintService service : services) {
                    System.out.println("["+service.getName()+"]");
                    if (service.getName().indexOf("ZDesigner") >= 0) {
                        ZplPrinterRobot p = new ZplPrinterRobot(service.getName());

                    }
                }
            }
            throw new RRException("没有找到打印机：["+printerURI+"]");
        }else{
            System.out.println("找到打印机：["+printerURI+"]");
            System.out.println("打印机名称：["+printService.getAttribute(PrinterName.class).getValue()+"]");
        }
    }

    public static void cancelPrint(String printer) {
        ZplPrinterRobot p = new ZplPrinterRobot(printer);
        p.cancel();
        String zpl = p.getZpl();
        p.print(zpl);
        p.print(zpl);
        p.print(zpl);
        p.print(zpl);
        p.print(zpl);
        p.print(zpl);
        p.print(zpl);
        p.print(zpl);
        p.print(zpl);
        p.print(zpl);
        System.out.println("取消成功");
    }



    private void cancel() {
        begin = "";
        end = "";
        content = "~JA";
    }

    /**
     * 打印标签
     */
    public static void printTag2(List<SysPlanBetEntity> planBets,Integer tag) {
        ZplPrinterRobot p = new ZplPrinterRobot(null);
        switch(tag){
            case 1:
                System.out.println("1.方牌高线厂");
                for(SysPlanBetEntity planBet:planBets){
                    System.out.println(1);

                    if(planBet.getTargetName()==null || "".equals(planBet.getTargetName())){
                        throw new RRException("产品名称不能为空");
                    }
                    if(planBet.getMark()==null || "".equals(planBet.getMark())){
                        throw new RRException("牌号不能为空");
                    }
                    if(planBet.getNorms()==null || "".equals(planBet.getNorms())){
                        throw new RRException("规格不能为空");
                    }
                    if(planBet.getMatNo()==null || "".equals(planBet.getMatNo())){
                        throw new RRException("材料号不能为空");
                    }
                    if(planBet.getProdDate()==null || "".equals(planBet.getProdDate())){
                        throw new RRException("生产日期不能为空");
                    }
                    if(planBet.getLicNo()==null ||"".equals(planBet.getLicNo())){
                        throw new RRException("许可证号不能为空");
                    }
                    if(planBet.getExeSta()==null ||"".equals(planBet.getExeSta())){
                        throw new RRException("执行标准不能为空");
                    }
                    if(planBet.getTaskId()==null || "".equals(planBet.getTaskId())){
                        throw new RRException("轧制批号不能为空");
                    }
                    if(planBet.getMatPraWei()==null||"".equals(planBet.getMatPraWei())){
                        throw new RRException(planBet.getMatNo()+"实重不能为空");
                    }
                    if(planBet.getClassCode()==null ||"".equals(planBet.getClassCode())){
                        throw new RRException("生产班组不能为空");
                    }

                    Integer MatPraWei = planBet.getMatPraWei().setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
                    System.out.println("不保留小数"+MatPraWei);
                    String targetName = planBet.getTargetName();
                    if(targetName.length()> 6){
                        String targetName1 = targetName.substring(0,6);
                        p.setTextR(targetName1, 385, 240, 25, 25);
                        String targetName2 =targetName.substring(6);
                        p.setTextR(targetName2, 360, 240, 25, 25);
                    }else{
                        p.setTextR(planBet.getTargetName(), 370, 240, 25, 25);//产品名称：混凝土用热轧带肋钢筋（系统带出）
                    }

                    //参数1 ：从上到下  y   参数2：从左到右  x    参数3：字体宽      参数4：字体高
                    p.setTextR(planBet.getMark(),310, 240, 30, 30);
                    p.setTextR(planBet.getNorms(), 255, 240, 30, 30);// 规    格：Φ8
                    p.setTextR(planBet.getMatNo(), 200, 240, 30, 30);//支数/捆号：D174000101（注：对应系统里的材料号）
                    SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd");
                    p.setTextR(sdf.format(planBet.getProdDate()), 145, 240, 30, 30);//生产日期：2017-04-08
                    p.setTextR(planBet.getLicNo(), 90, 240, 30, 30); //  许可证号：XK05-001-00276（系统带出）
                    p.setTextR(planBet.getExeSta(), 370, 590, 30, 30); //执行标准：GB1499.2 （注：不带年份）
                    p.setTextR(planBet.getTaskId(), 300, 590, 30, 30);// 炉 批 号：D1740001（注：对应系统里的轧制批号）
                    if("1".equals( planBet.getBuilding() )){//building 字段为1  炉号打印/
                        planBet.setStoNo("/");
                    }
                    p.setTextR(planBet.getStoNo(), 255, 590, 30, 30);//炉号
                    p.setTextR(Integer.toString(MatPraWei), 255, 590, 30, 30);//重量：实际重量
                    p.setTextR(planBet.getClassCode()+" "+planBet.getClassCode2(),  200,590, 30, 30);            // 生产班组：甲（注：填写 甲 乙 丙）

                    //条码
                    String bar2 = planBet.getMatNo();//12位
                    String bar2Paper = "^FO23,200^BY2,3.0,50^BCR,,Y,N,N^FD${data}^FS";//条码样式模板
                    p.setBarcode(bar2,bar2Paper);

                    //二维码
                    String bar3 ="产品名称:"+planBet.getTargetName()+" 重量:"+Double.toString(MatPraWei)+"kg"+"规格:"+planBet.getNorms()+"牌号:"+planBet.getMark();//12位^CI26
                    String bar3Paper ="^FO18,558^BQ,2,3^CI26^FH^FDQA,${data}^FS";//二维码样式模板
                    p.setBarcode(bar3,bar3Paper);

                    String zpl = p.getZpl();
                    System.out.println("zpl:" + zpl);

//                    boolean result = p.print(zpl);//打印
//                    if(!result){
//                        throw new RRException("第一次打印出错");
//                    }
//                    result = p.print(zpl);//打印
//                    if(!result){
//                        throw new RRException("第二次打印出错");
//                    }
//                    p.resetZpl();//注意要清除上次的打印信息
                }
                break;

        }


    }


    /**
     * 打印标签
     */
    public static void printTag(String printer,List<SysPlanBetEntity> planBets,Integer tag) {
        ZplPrinterRobot p = new ZplPrinterRobot(printer);
        switch(tag){
            case 1:
                System.out.println("1.方牌高线厂");
                for(SysPlanBetEntity planBet:planBets){
                    System.out.println(1);

                    if(planBet.getTargetName()==null || "".equals(planBet.getTargetName())){
                        throw new RRException("产品名称不能为空");
                    }
                    if(planBet.getMark()==null || "".equals(planBet.getMark())){
                        throw new RRException("牌号不能为空");
                    }
                    if(planBet.getNorms()==null || "".equals(planBet.getNorms())){
                        throw new RRException("规格不能为空");
                    }
                    if(planBet.getMatNo()==null || "".equals(planBet.getMatNo())){
                        throw new RRException("材料号不能为空");
                    }
                    if(planBet.getProdDate()==null || "".equals(planBet.getProdDate())){
                        throw new RRException("生产日期不能为空");
                    }
                    if(planBet.getLicNo()==null ||"".equals(planBet.getLicNo())){
                        throw new RRException("许可证号不能为空");
                    }
                    if(planBet.getExeSta()==null ||"".equals(planBet.getExeSta())){
                        throw new RRException("执行标准不能为空");
                    }
                    if(planBet.getTaskId()==null || "".equals(planBet.getTaskId())){
                        throw new RRException("轧制批号不能为空");
                    }
                    if(planBet.getMatPraWei()==null||"".equals(planBet.getMatPraWei())){
                        throw new RRException(planBet.getMatNo()+"实重不能为空");
                    }
                    if(planBet.getClassCode()==null ||"".equals(planBet.getClassCode())){
                        throw new RRException("生产班组不能为空");
                    }

                    Integer MatPraWei = planBet.getMatPraWei().setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
                    System.out.println("不保留小数"+MatPraWei);
                    String targetName = planBet.getTargetName();
                    if(targetName.length()> 6){
                        String targetName1 = targetName.substring(0,6);
                        p.setTextR(targetName1, 385, 240, 25, 25);
                        String targetName2 =targetName.substring(6);
                        p.setTextR(targetName2, 360, 240, 25, 25);
                    }else{
                        p.setTextR(planBet.getTargetName(), 370, 240, 25, 25);//产品名称：混凝土用热轧带肋钢筋（系统带出）
                    }

                    //参数1 ：从上到下  y   参数2：从左到右  x    参数3：字体宽      参数4：字体高
                    p.setTextR(planBet.getMark(),310, 240, 30, 30);
                    //牌    号：HRB400
                    p.setTextR(planBet.getNorms(), 255, 240, 30, 30);                // 规    格：Φ8
                    p.setTextR(planBet.getMatNo(), 200, 240, 30, 30);                //支数/捆号：D174000101（注：对应系统里的材料号）
                    SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd");
                    p.setTextR(sdf.format(planBet.getProdDate()), 145, 240, 30, 30);  //生产日期：2017-04-08
                    p.setTextR(planBet.getLicNo(), 90, 240, 30, 30);                //  许可证号：XK05-001-00276（系统带出）
                    p.setTextR(planBet.getExeSta(), 370, 590, 30, 30);               //执行标准：GB1499.2 （注：不带年份）
                    p.setTextR(planBet.getTaskId(), 300, 590, 30, 30);				  // 炉 批 号：D1740001（注：对应系统里的轧制批号）

                    if("1".equals( planBet.getBuilding() )){//building 字段为1  炉号打印/
                        planBet.setStoNo("/");
                    }
                    p.setTextR(planBet.getStoNo(), 255, 590, 30, 30);//炉号
                    p.setTextR(Integer.toString(MatPraWei), 255, 590, 30, 30);        //重    量：实际重量
                    p.setTextR(planBet.getClassCode()+" "+planBet.getClassCode2(),  200,590, 30, 30);            // 生产班组：甲（注：填写 甲 乙 丙）

                    //条码
                    String bar2 = planBet.getMatNo();//12位
                    String bar2Paper = "^FO23,200^BY2,3.0,50^BCR,,Y,N,N^FD${data}^FS";//条码样式模板
                    p.setBarcode(bar2,bar2Paper);

                    //二维码
                    String bar3 ="产品名称:"+planBet.getTargetName()+" 重量:"+Double.toString(MatPraWei)+"kg"+"规格:"+planBet.getNorms()+"牌号:"+planBet.getMark();//12位^CI26
                    String bar3Paper ="^FO18,558^BQ,2,3^CI26^FH^FDQA,${data}^FS";//二维码样式模板 QA 高可靠性级别
                    p.setBarcode(bar3,bar3Paper);

                    String zpl = p.getZpl();
                    System.out.println("zpl:" + zpl);

                    boolean result = p.print(zpl);//打印
                    if(!result){
                        throw new RRException("第一次打印出错");
                    }
                    result = p.print(zpl);//打印
                    if(!result){
                        throw new RRException("第二次打印出错");
                    }
                    p.resetZpl();//注意要清除上次的打印信息
                }
                break;

        }


    }

    /**
     * 设置条形码
     * @param barcode 条码字符
     * @param zpl 条码样式模板
     */
    public void setBarcode(String barcode,String zpl) {
        content += zpl.replace("${data}", barcode);
    }

    /**
     * 中文字符、英文字符(包含数字)混合
     * @param str 中文、英文
     * @param x x坐标
     * @param y y坐标
     * @param eh 英文字体高度height
     * @param ew 英文字体宽度width
     * @param es 英文字体间距spacing
     * @param mx 中文x轴字体图形放大倍率。范围1-10，默认1
     * @param my 中文y轴字体图形放大倍率。范围1-10，默认1
     * @param ms 中文字体间距。24是个比较合适的值。
     */
	public void setText(String str, int x, int y, int eh, int ew, int es, int mx, int my, int ms) {
		byte[] ch = str2bytes(str);
		for (int off = 0; off < ch.length;) {
			if (((int) ch[off] & 0x00ff) >= 0xA0) {
				int qcode = ch[off] & 0xff;
				int wcode = ch[off + 1] & 0xff;
				content += String.format("^FO%d,%d^XG0000%01X%01X,%d,%d^FS\n", x, y, qcode, wcode, mx, my);
				begin += String.format("~DG0000%02X%02X,00072,003,\n", qcode, wcode);
				qcode = (qcode + 128 - 32) & 0x00ff;
				wcode = (wcode + 128 - 32) & 0x00ff;
				int offset = ((int) qcode - 16) * 94 * 72 + ((int) wcode - 1) * 72;
				for (int j = 0; j < 72; j += 3) {
					qcode = (int) dotFont[j + offset] & 0x00ff;
					wcode = (int) dotFont[j + offset + 1] & 0x00ff;
					int qcode1 = (int) dotFont[j + offset + 2] & 0x00ff;
					begin += String.format("%02X%02X%02X\n", qcode, wcode, qcode1);
				}
				x = x + ms * mx;
				off = off + 2;
			} else if (((int) ch[off] & 0x00FF) < 0xA0) {
				setChar(String.format("%c", ch[off]), x, y, eh, ew);
				x = x + es;
				off++;
			}
		}
	}

    /**
     * 中文字符、英文字符(包含数字)混合
     * ^CWJ,E:ANMDS.TTF^LH0,0^FOx,y^AJN,H,W^CI28^FDXX^FS
     *
     * @param str 中文、英文
     * @param x x坐标
     * @param y y坐标
     * @param h 字体高度height
     * @param w 字体宽度width
     */
    public void setText(String str, int x, int y , int h, int w) {
        content += "^CWJ,E:ANMDS.TTF^LH0,0"+"^FO"+x+","+y+"^AJN,"+h+","+w+"^CI28^FD"+str+"^FS";
    }

    /**
     * 中文字符、英文字符(包含数字)混合 转180度
     * ^AJB：旋转180°
     * ^FO：往左上角打印
     * @param str 中文、英文
     * @param x x坐标
     * @param y y坐标
     * @param h 字体高度height
     * @param w 字体宽度width
     */
    public void setTextR(String str, int x, int y , int h, int w) {
        content += "^CWJ,E:ANMDS.TTF^FB600,1,0,L,0^LH0,0"+"^FO"+x+","+y+"^AJB,"+h+","+w+"^CI28^FD"+str+"^FS";
    }

    /**
     * 中文字符、英文字符(包含数字) 往左下角打印
     * "^FT:往左下角打印
     * @param str 中文、英文
     * @param x x坐标
     * @param y y坐标
     * @param h 字体高度height
     * @param w 字体宽度width
     */
    public void setTextLT(String str, int x, int y , int h, int w) {
        content += "^CWJ,E:ANMDS.TTF^FB600,1,0,L,0^LH0,0"+"^FT"+x+","+y+"^AJB,"+h+","+w+"^CI28^FD"+str+"^FS";
    }
    /**
     * 英文字符串(包含数字)
     * @param str 英文字符串
     * @param x	x坐标
     * @param y y坐标
     * @param h 高度
     * @param w 宽度
     */
    public void setChar(String str, int x, int y, int h, int w) {
        content += "^FO" + x + "," + y + "^A0," + h + "," + w + "^FD" + str + "^FS";
    }
    /**
     * 英文字符(包含数字)顺时针旋转90度
     * @param str 英文字符串
     * @param x	x坐标
     * @param y y坐标
     * @param h 高度
     * @param w 宽度
     */
    public void setCharR(String str, int x, int y, int h, int w) {
        content += "^FO" + x + "," + y + "^A0R," + h + "," + w + "^FD" + str + "^FS";
    }
    /**
     * 获取完整的ZPL
     * @return
     */
    public String getZpl() {
        return begin + content + end;
    }
    /**
     * 重置ZPL指令，当需要打印多张纸的时候需要调用。
     */
    public void resetZpl() {
        begin = "^XA";
        end = "^XZ";
        content = "";
    }
    /**
     * 打印
     * @param zpl 完整的ZPL
     */
    public boolean print(String zpl){
        if(printService==null){
            System.out.println("打印出错：没有找到打印机：["+printerURI+"]");
            return false;
        }
        DocPrintJob job = printService.createPrintJob();
        byte[] by=str2bytes(zpl);//要转为utf-8
        DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
        Doc doc = new SimpleDoc(by, flavor, null);
        try {
            job.print(doc, null);
            System.out.println("已打印");
            return true;
        } catch (PrintException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 字符串转byte[]
     * @param s
     * @return
     */
    private byte[] str2bytes(String s) {
        if (null == s || "".equals(s)) {
            return null;
        }
        byte[] abytes = null;
        try {
            abytes = s.getBytes("utf-8");
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }
        return abytes;
    }


}
